--- /dev/null
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const IBus = imports.gi.IBus;
+const Lang = imports.lang;
+const St = imports.gi.St;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+
+const MAX_CANDIDATES_PER_PAGE = 16;
+
+const CandidateArea = new Lang.Class({
+ Name: 'CandidateArea',
+ Extends: PopupMenu.PopupBaseMenuItem,
+
+ _init: function() {
+ this.parent({ reactive: false });
+
+ // St.Table exhibits some sizing problems so let's go with a
+ // clutter layout manager for now.
+ this._table = new Clutter.Actor();
+ this.addActor(this._table);
+
+ this._tableLayout = new Clutter.TableLayout();
+ this._table.set_layout_manager(this._tableLayout);
+
+ this._indexLabels = [];
+ this._candidateLabels = [];
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
+ this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
+ }
+
+ this._orientation = -1;
+ this._cursorPosition = 0;
+ },
+
+ _setOrientation: function(orientation) {
+ if (this._orientation == orientation)
+ return;
+
+ this._orientation = orientation;
+
+ this._table.remove_all_children();
+
+ if (this._orientation == IBus.Orientation.HORIZONTAL)
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ this._tableLayout.pack(this._indexLabels[i], i*2, 0);
+ this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
+ }
+ else // VERTICAL || SYSTEM
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ this._tableLayout.pack(this._indexLabels[i], 0, i);
+ this._tableLayout.pack(this._candidateLabels[i], 1, i);
+ }
+ },
+
+ setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
+ this._setOrientation(orientation);
+
+ for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
+ let visible = i < candidates.length;
+ this._indexLabels[i].visible = visible;
+ this._candidateLabels[i].visible = visible;
+
+ if (!visible)
+ continue;
+
+ this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
+ this._candidateLabels[i].text = candidates[i];
+ }
+
+ this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
+ this._cursorPosition = cursorPosition;
+ if (cursorVisible)
+ this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
+ },
+});
+
+const CandidatePopup = new Lang.Class({
+ Name: 'CandidatePopup',
+ Extends: PopupMenu.PopupMenu,
+
+ _init: function() {
+ this._cursor = new St.Bin({ opacity: 0 });
+ Main.uiGroup.add_actor(this._cursor);
+
+ this.parent(this._cursor, 0, St.Side.TOP);
+ this.actor.hide();
+ Main.uiGroup.add_actor(this.actor);
+
+ this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
+ this._preeditTextItem.actor.hide();
+ this.addMenuItem(this._preeditTextItem);
+
+ this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
+ this._auxTextItem.actor.hide();
+ this.addMenuItem(this._auxTextItem);
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._lookupTableItem = new CandidateArea();
+ this._lookupTableItem.actor.hide();
+ this.addMenuItem(this._lookupTableItem);
+
+ this._panelService = null;
+ },
+
+ setPanelService: function(panelService) {
+ this._panelService = panelService;
+ if (!panelService)
+ return;
+
+ panelService.connect('set-cursor-location',
+ Lang.bind(this, function(ps, x, y, w, h) {
+ this._cursor.set_position(x, y);
+ this._cursor.set_size(w, h);
+ }));
+ panelService.connect('update-preedit-text',
+ Lang.bind(this, function(ps, text, cursorPosition, visible) {
+ if (visible)
+ this._preeditTextItem.actor.show();
+ else
+ this._preeditTextItem.actor.hide();
+ this._updateVisibility();
+
+ this._preeditTextItem.actor.label_actor.text = text.get_text();
+
+ let attrs = text.get_attributes();
+ if (attrs)
+ this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
+ attrs);
+ }));
+ panelService.connect('show-preedit-text',
+ Lang.bind(this, function(ps) {
+ this._preeditTextItem.actor.show();
+ this._updateVisibility();
+ }));
+ panelService.connect('hide-preedit-text',
+ Lang.bind(this, function(ps) {
+ this._preeditTextItem.actor.hide();
+ this._updateVisibility();
+ }));
+ panelService.connect('update-auxiliary-text',
+ Lang.bind(this, function(ps, text, visible) {
+ if (visible)
+ this._auxTextItem.actor.show();
+ else
+ this._auxTextItem.actor.hide();
+ this._updateVisibility();
+
+ this._auxTextItem.actor.label_actor.text = text.get_text();
+ }));
+ panelService.connect('show-auxiliary-text',
+ Lang.bind(this, function(ps) {
+ this._auxTextItem.actor.show();
+ this._updateVisibility();
+ }));
+ panelService.connect('hide-auxiliary-text',
+ Lang.bind(this, function(ps) {
+ this._auxTextItem.actor.hide();
+ this._updateVisibility();
+ }));
+ panelService.connect('update-lookup-table',
+ Lang.bind(this, function(ps, lookupTable, visible) {
+ if (visible)
+ this._lookupTableItem.actor.show();
+ else
+ this._lookupTableItem.actor.hide();
+ this._updateVisibility();
+
+ let cursorPos = lookupTable.get_cursor_pos();
+ let pageSize = lookupTable.get_page_size();
+ let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
+ let startIndex = page * pageSize;
+ let endIndex = Math.min((page + 1) * pageSize,
+ lookupTable.get_number_of_candidates());
+ let indexes = [];
+ let indexLabel;
+ for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
+ indexes.push(indexLabel.get_text());
+
+ let candidates = [];
+ for (let i = startIndex; i < endIndex; ++i)
+ candidates.push(lookupTable.get_candidate(i).get_text());
+
+ this._lookupTableItem.setCandidates(indexes,
+ candidates,
+ lookupTable.get_orientation(),
+ cursorPos % pageSize,
+ lookupTable.is_cursor_visible());
+ }));
+ panelService.connect('show-lookup-table',
+ Lang.bind(this, function(ps) {
+ this._lookupTableItem.actor.show();
+ this._updateVisibility();
+ }));
+ panelService.connect('hide-lookup-table',
+ Lang.bind(this, function(ps) {
+ this._lookupTableItem.actor.hide();
+ this._updateVisibility();
+ }));
+ panelService.connect('focus-out',
+ Lang.bind(this, function(ps) {
+ this.close(BoxPointer.PopupAnimation.NONE);
+ }));
+ },
+
+ _updateVisibility: function() {
+ let isVisible = (this._preeditTextItem.actor.visible ||
+ this._auxTextItem.actor.visible ||
+ this._lookupTableItem.actor.visible);
+
+ if (isVisible)
+ this.open(BoxPointer.PopupAnimation.NONE);
+ else
+ this.close(BoxPointer.PopupAnimation.NONE);
+ },
+
+ _setTextAttributes: function(clutterText, ibusAttrList) {
+ let attr;
+ for (let i = 0; attr = ibusAttrList.get(i); ++i)
+ if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
+ clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
+ }
+});
var IBus = imports.gi.IBus;
if (!('new_async' in IBus.Bus))
throw "IBus version is too old";
+ const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
} catch (e) {
var IBus = null;
log(e);
IBus.init();
this._readyCallback = readyCallback;
+ this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
this._ibus = null;
+ this._panelService = null;
this._engines = {};
this._ready = false;
},
_clear: function() {
+ if (this._panelService)
+ this._panelService.destroy();
if (this._ibus)
this._ibus.destroy();
this._ibus = null;
+ this._panelService = null;
+ this._candidatePopup.setPanelService(null);
this._engines = {};
this._ready = false;
},
_onConnected: function() {
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
+ this._ibus.request_name_async(IBus.SERVICE_PANEL,
+ IBus.BusNameFlag.REPLACE_EXISTING,
+ -1, null,
+ Lang.bind(this, this._initPanelService));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
},
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
- this._ready = true;
- if (this._readyCallback)
- this._readyCallback();
} else {
this._clear();
+ return;
+ }
+
+ this._updateReadiness();
+ },
+
+ _initPanelService: function(ibus, result) {
+ let success = this._ibus.request_name_async_finish(result);
+ if (success) {
+ this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
+ object_path: IBus.PATH_PANEL });
+ this._candidatePopup.setPanelService(this._panelService);
+ } else {
+ this._clear();
+ return;
}
+
+ this._updateReadiness();
+ },
+
+ _updateReadiness: function() {
+ this._ready = (Object.keys(this._engines).length > 0 &&
+ this._panelService != null);
+
+ if (this._ready && this._readyCallback)
+ this._readyCallback();
},
getEngineDesc: function(id) {